package com.sxhuayuan.parking.plugin.parking.dao_er;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sxhuayuan.parking.compenent.Message;
import com.sxhuayuan.parking.compenent.domain.Setting;
import com.sxhuayuan.parking.compenent.web.RespMessage;
import com.sxhuayuan.parking.config.Platform;
import com.sxhuayuan.parking.dto.CreateMcardChargeOrderDTO;
import com.sxhuayuan.parking.dto.ParkFeeCacheBean;
import com.sxhuayuan.parking.entity.*;
import com.sxhuayuan.parking.exception.MyException;
import com.sxhuayuan.parking.exception.SystemException;
import com.sxhuayuan.parking.plugin.parking.ParkingPlugin;
import com.sxhuayuan.parking.plugin.parking.dao_er.dto.EntranceNoticeDto;
import com.sxhuayuan.parking.plugin.parking.dao_er.dto.ExportNoticeDto;
import com.sxhuayuan.parking.plugin.parking.dao_er.request.DaoerBaseRequest;
import com.sxhuayuan.parking.plugin.parking.dao_er.request.DaoerOrderSuccessNotifyRequestData;
import com.sxhuayuan.parking.plugin.parking.dao_er.request.DaoerQueryLeavingCarParkingFeeRequestData;
import com.sxhuayuan.parking.plugin.parking.dao_er.request.DaoerQueryParkingFeeRequestData;
import com.sxhuayuan.parking.plugin.parking.result.*;
import com.sxhuayuan.parking.queue.msg.NotifyParkingLotMsg;
import com.sxhuayuan.parking.service.*;
import com.sxhuayuan.parking.utils.DateUtils;
import com.sxhuayuan.parking.utils.Md5Utils;
import com.sxhuayuan.parking.utils.SettingUtils;
import com.sxhuayuan.parking.utils.SpringUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.util.*;

import static com.sxhuayuan.parking.utils.BeanUtils.jsonBean2Map;

/**
 * 道尔
 * 
 * @author 何龙
 * @date 2019-07-15 10:15
 */
@Component(DaoerParkingPlugin.ID)
public class DaoerParkingPlugin extends ParkingPlugin {
	public static final String ID = "daoerParkingPlugin";
	/** 接口访问成功状态码 */
	private static final int SUCCESS_STATUS = 1;
	/** 签名时间戳超时时间-秒 */
	private static final int TIMEOUT_SECONDS = 60;
	private Logger log = LoggerFactory.getLogger(getClass());

	@Autowired
	ParkingLotService lotService;
	@Autowired
	ParkChannelService exitService;
	@Autowired
	OrderService orderService;
	@Autowired
	ParkingInService inService;
	@Autowired
	ParkingInOutService inOutService;

	@Override
	public String getId() {
		return ID;
	}

	@Override
	public String getName() {
		return "道尔";
	}

	@Override
	public ParkFeeCacheBean saveParkInRecord(String lotSn, String carPlate, String resp) {
		JSONObject httpResp = JSON.parseObject(resp).getJSONObject("body");
		ParkingLot lot = lotService.findBySn(lotSn);
		carPlate = httpResp.getString("carNo");
		Date inTime = DateUtils.parse(httpResp.getString("entryTime"));
		Date chargeTime = DateUtils.parse(httpResp.getString("chargeTime"));
		ParkingIn parkingIn = inService.findByCarPlate(lot.getId(), httpResp.getString("carNo"));
		if (parkingIn == null) {
			parkingIn = inService.enter(lot, carPlate, inTime, null, lot.getFreeMinutes(), null);
		}
		ParkFeeCacheBean bean = new ParkFeeCacheBean();
		bean.setParkPluginId(ID);
		bean.setEndTime(chargeTime);
		bean.setLotOrderNo(null);
		bean.setDiscountFee(httpResp.getFloatValue("discountAmount"));
		bean.setTotalFee(httpResp.getFloatValue("amount"));
		bean.setUniqueId(parkingIn.getUniqueId());
		return bean;
	}

	@Override
	public QueryParkingFeeResultWrap queryParkingFee(ParkingLot lot, String carPlate) {
		log.debug("调用接口查询停车费: lotSn={}, carPlate={}", lot, carPlate);
		DaoerQueryParkingFeeRequestData feeRequest = new DaoerQueryParkingFeeRequestData();
		feeRequest.setCarNo(carPlate);
		QueryParkingFeeResultWrap rs = new QueryParkingFeeResultWrap();
		try {
			String url = feeRequest.checkAndSign(lot);
			String result = send(url, feeRequest);
			log.debug(result);
			if (StringUtils.isEmpty(result)) {
				rs.setIsSuccess(false);
				rs.setMsg("查询失败");
			} else {
				JSONObject object = JSON.parseObject(result);
				if (isSuccess(result)) {
					JSONObject respData = object.getJSONObject("body");
					// 超时缴费
					if (respData.getIntValue("paymentType") == 1 && respData.getFloatValue("amount") == 0) {
						rs.setIsSuccess(false);
						rs.setMsg("已经缴过费，未离场");
						return rs;
					}
					rs.setEntertime(respData.getString("entryTime"));
					rs.setCalcStartTime(respData.getString("chargeTime"));
					rs.setCalcEndTime(DateUtils.format(new Date()));
					rs.setCardNo(respData.getString("cardNo"));
					rs.setCarPlate(carPlate);
					// 总计
					float amount = respData.getFloatValue("amount");
					float discountAmount = respData.getFloatValue("discountAmount");
					float theActual = SettingUtils.get().setScale(new BigDecimal(amount).subtract(new BigDecimal(discountAmount))).floatValue();
					rs.setCalcFee(amount);
					// 实收=总计-优惠
					rs.setFee(theActual);
					rs.setExtraMchid(lot.getSn());
					rs.setParktimeMinute(respData.getInteger("chargeDuration"));
					rs.setExtraPluginId(ID);
					// 车场订单号
					rs.setLotOrderNo(null);
					// 车场流水号
					rs.setNo(null);
					rs.setRules(null);

					Long uniqueId = submitUpdateOriginalInfoTask(lot.getSn(), carPlate, theActual, result);
					rs.setUniqueId(uniqueId != null ? String.valueOf(uniqueId) : "");
				} else {
					rs.setIsSuccess(false);
					rs.setMsg(object.getJSONObject("head").getString("message"));
					return rs;
				}
			}
		} catch (SystemException e) {
			rs.setIsSuccess(false);
			rs.setMsg(e.getMessage());
			return rs;
		}
		return rs;
	}

	@Override
	public QueryParkingFeeResultWrap queryLeavingCarParkingLotOrder(ParkingLot lot, String exitSn, Member member) {
		log.debug("调用车场通知车辆出场接口获取车辆信息: exitSn={}", exitSn);
		long bt = System.currentTimeMillis();
		ParkChannel exit = exitService.findBySn(exitSn);
		String lotSn = lot.getSn();
		DaoerQueryLeavingCarParkingFeeRequestData request = new DaoerQueryLeavingCarParkingFeeRequestData();
		QueryParkingFeeResultWrap rs = new QueryParkingFeeResultWrap();
		try {
			request.setUserId(member.getOpenid());
			if (StringUtils.isEmpty(exit.getChannelSn())) {
				throw new SystemException("通道编号未配置");
			}
			request.setChannelId(exit.getChannelSn());
			String url = request.checkAndSign(lot);
			String result = send(url, request);
			if (StringUtils.isEmpty(result)) {
				rs.setIsSuccess(false);
				rs.setMsg("接口请求失败");
			} else {
				JSONObject object = JSON.parseObject(result);
				if (isSuccess(result)) {
					JSONObject respData = object.getJSONObject("body");
					rs.setCardNo(null);
					rs.setCarPlate(respData.getString("CarNo"));
					// 总计
					rs.setCalcFee(Float.valueOf(respData.getString("TotalAmount")));
					// 实收=总计-优惠
					Float fee = Float.valueOf(respData.getString("CouponAmount"));
					rs.setFee(fee);
					rs.setCalcStartTime(respData.getString("CarInTime"));
					rs.setEntertime(respData.getString("CarInTime"));
					rs.setCalcEndTime(DateUtils.format(new Date()));
					rs.setExtraMchid(lotSn);
					rs.setParktimeMinute(Integer.valueOf(respData.getString("Duration")));
					rs.setExtraPluginId(ID);
					// 车场订单号
					rs.setLotOrderNo(respData.getString("ID"));
					// 车场流水号
					rs.setNo(null);
					rs.setRules(null);
					Long uniqueId = submitUpdateOriginalInfoTask(lotSn, rs.getCarPlate(), fee, result);
					rs.setUniqueId(uniqueId != null ? String.valueOf(uniqueId) : "");
					log.debug("execute time: {}", System.currentTimeMillis() - bt);
				} else {
					rs.setIsSuccess(false);
					rs.setMsg(object.getJSONObject("head").getString("message"));
				}
			}
		} catch (Exception e) {
			rs.setIsSuccess(false);
			rs.setMsg(e.getMessage());
			return rs;
		}
		return rs;
	}

	@Override
	public boolean notifyParkingFeeOrderSuccess(NotifyParkingLotMsg notify) {
		String orderSn = notify.getLocalOrderSn();
		String lotSn = notify.getLocalLotSn();
		String carPlate = notify.getCarPlate();
		log.debug("通知车场: orderSn = {}", orderSn);
		Order order = orderService.findBySn(orderSn);
		ParkingLot parkingLot = lotService.findBySn(lotSn);
		Setting setting = SettingUtils.get();
		if (order != null) {
			try {
				DaoerOrderSuccessNotifyRequestData data = new DaoerOrderSuccessNotifyRequestData();
				data.setCarNo(carPlate);
				data.setEntryTime(DateUtils.format(order.getEntertime()));
				data.setPayTime(DateUtils.format(order.getPaytime()));
				data.setChargeDuration(order.getParkTime().toString());
				BigDecimal money = order.getMoney();
				BigDecimal totalDiscount = order.getTotalDiscount();
				BigDecimal amount = money.add(totalDiscount);
				data.setAmount(setting.setScale(amount).toString());
				data.setPurchaseAmount(setting.setScale(money).toString());
				data.setCouponAmount(setting.setScale(totalDiscount).toString());
				data.setPaymentType(getPayType(order.getPlatform()));
				data.setPaymentTnx(order.getSn());
				data.setOperator(order.getMemberId().toString());
				// 出口扫码时需要出口编号
				if (order.getMethod() == Order.Method.exit) {
					ParkChannel exit = exitService.findOne(order.getExitId());
					if (exit == null || exit.getChannelSn() == null) {
						throw new SystemException("出口不存在或出口编号未配置");
					}
					data.setChannelId(exit.getChannelSn());
				}
				String url = data.checkAndSign(parkingLot);

				String resp = send(url, data);
				if (resp == null) {
					throw new MyException(RespMessage.Status.ERROR, Message.COMMON_SERVER_CONNECTION_FAILURE);
				}
				boolean success = isSuccess(resp);
				if (success) {
					orderService.notifySuccess(order);
				}
				return success;
			} catch (Exception e) {
				throw new RuntimeException(e.getMessage());
			}
		}
		throw new RuntimeException(SpringUtils.getMessage(Message.ORDER_NOT_EXISTS));
	}

	@Override
	public boolean notifyMonthCardChargeOrderSuccess(String order, String lotSn, String carPlate) {
		return false;
	}

	@Override
	public McardQueryResultWrap monthCardQuery(String lotSn, MemberCarPlate carPlate) {
		return null;
	}

	@Override
	public McardQueryResultWrap monthCardChargePreview(String lotSn, String carPlate, Integer month) {
		return null;
	}

	@Override
	public ParkingLotInfoQueryResultWrap parkingLotInfoQuery(String lotSn) {
		return null;
	}

	@Override
	public McardQueryChargeOptionsResultWrap monthCardQueryChargeOptions(String lotSn) {
		return null;
	}

	@Override
	public McardChargeOrderCreateResultWrap monthCardCreateOrder(MemberCarPlate memberCarPlate, CreateMcardChargeOrderDTO dto, Platform platform) {
		return null;
	}

	@Override
	public McardCancelResultWrap monthCardCancel(String lotSn, String carPlate) {
		return null;
	}

	@Override
	public String execute(String api, String lotSn, Map map) {
		return null;
	}

	@Override
	public void afterInNoticeHanler(HttpServletRequest request, HttpServletResponse response, ParkingLot parkingLot) {
		try {
			String s = IOUtils.toString(request.getInputStream(), "utf-8");
			if (isRequestSuccess(NoticeType.afterin, s, parkingLot)) {
				responseWrite(response, returnErr("请求失败,请检查签名或者参数是否正确"));
				return;
			}
			String carinlist = JSON.parseObject(s).getString("carinlist");
			List<EntranceNoticeDto> list = JSON.parseArray(carinlist, EntranceNoticeDto.class);
			List<String> ids = new ArrayList<>();
			for (EntranceNoticeDto entrance : list) {
				try {
					String carNo = entrance.getCarNo();
					if (StringUtils.isEmpty(carNo)) {
						log.debug("暂不支持无牌车入场本系统，编号：{}", entrance.getCardNo());
						continue;
					}
					ParkChannel parkChannel = exitService.findByParkingLotAndTypeAndName(parkingLot, ParkChannel.Type.entrance, entrance.getEntrance());
					// 车辆入场
					inService.enter(parkingLot, entrance.getCarNo(), DateUtils.parse(entrance.getEntranceTime()), parkChannel == null ? null : parkChannel.getId(),
							parkingLot.getFreeMinutes(), null);
					ids.add(entrance.getId());
					log.debug("【{}】进入车场：{}", entrance.getCarNo(), parkingLot.getName());
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			responseWrite(response, returnSuccess(ids));
		} catch (IOException e) {
			e.printStackTrace();
			responseWrite(response, returnErr(e.getMessage()));
		}
	}

	@Override
	public void afterOutNoticeHanler(HttpServletRequest request, HttpServletResponse response, ParkingLot parkingLot) {
		try {
			String s = IOUtils.toString(request.getInputStream(), "utf-8");
			if (isRequestSuccess(NoticeType.afterout, s, parkingLot)) {
				responseWrite(response, returnErr("请求失败,请检查签名或者参数是否正确"));
				return;
			}
			String caroutlist = JSON.parseObject(s).getString("caroutlist");
			List<ExportNoticeDto> list = JSON.parseArray(caroutlist, ExportNoticeDto.class);
			List<String> ids = new ArrayList<>();
			for (ExportNoticeDto export : list) {
				try {
					ParkChannel parkChannel = exitService.findByParkingLotAndTypeAndName(parkingLot, ParkChannel.Type.exit, export.getExport());
					ParkingIn parkingIn = inService.findByCarPlate(parkingLot.getId(), export.getCarNo());
					if (parkingIn == null) {
						log.debug("【{}】出场失败,未找到入场记录", export.getCarNo());
						continue;
					}
					// 车辆出场
					inOutService.out(parkingIn, DateUtils.parse(export.getExportTime()), parkChannel == null ? null : parkChannel.getId());
					ids.add(export.getId());
					log.debug("【{}】驶离车场：{}", export.getCarNo(), parkingLot.getName());
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			responseWrite(response, returnSuccess(ids));
		} catch (IOException e) {
			e.printStackTrace();
			responseWrite(response, returnErr(e.getMessage()));
		}
	}
	
	@Override
	public void beforeOutNoticeHandler(HttpServletRequest request, HttpServletResponse response, ParkingLot parkingLot) {
		// TODO Auto-generated method stub
		
	}

	/**
	 * 判断请求是否有效，验证签名
	 * 
	 * @param type
	 *            通知类型
	 * @param requestStr
	 *            请求的字符串
	 * @param parkingLot
	 *            车场
	 * @return 为true为校验失败 为fase表示成功
	 */
	private boolean isRequestSuccess(NoticeType type, String requestStr, ParkingLot parkingLot) {
		Assert.notNull(type, "");
		Assert.notNull(parkingLot, "");
		if (StringUtils.isEmpty(requestStr)) {
			return true;
		}
		try {
			JSONObject object = JSON.parseObject(requestStr);
			String signature = object.getString("signature");
			String t = object.getString("t");
			Long time = Long.valueOf(t);
			// 时间戳超时，签名校验失败
			if (!SettingUtils.get().getIsDebug() && System.currentTimeMillis() / 1000 - time > TIMEOUT_SECONDS) {
				log.debug("签名校验失败：时间戳时间超时");
				return true;
			}
			if (StringUtils.isEmpty(signature) || StringUtils.isEmpty(t)) {
				log.debug("签名校验失败：签名字段为空");
				return true;
			}
			// 校验签名
			String url = null;
			if (type == NoticeType.afterin) {
				url = getNoticeUrl() + "/entrance/" + parkingLot.getSn();
			} else if (type == NoticeType.afterout) {
				url = getNoticeUrl() + "/export/" + parkingLot.getSn();
			}
			if (url != null) {
				// 根据道尔系统的要求拼凑MD5转码之前的地址，格式为requestString@#time@#key
				String s = Md5Utils.md5(String.format("%s@#%s@#%s", url, t, parkingLot.getApiKey()));
				return !signature.equals(s);
			}
			log.debug("签名校验失败");
			return true;
		} catch (Exception e) {
			log.debug("签名校验失败,系统异常:{}", e.getMessage());
			return true;
		}

	}

	/** 返回结果 */
	private void responseWrite(HttpServletResponse response, String msg) {
		response.setContentType("text/json;charset=UTF-8");
		response.setCharacterEncoding("UTF-8");
		PrintWriter out = null;
		try {
			out = response.getWriter();
		} catch (IOException e) {
			e.printStackTrace();
		}
		if (out != null) {
			out.println(msg);
			out.flush();
			out.close();
		}
	}

	/** 判断接口是否请求成功 */
	private boolean isSuccess(String resp) {
		try {
			if (StringUtils.isEmpty(resp)) {
				return false;
			} else {
				int status = JSON.parseObject(resp).getJSONObject("head").getIntValue("status");
				return status == SUCCESS_STATUS;
			}
		} catch (Exception e) {
			return false;
		}
	}

	/** 对应道尔接口的支付方式 */
	private int getPayType(Platform platform) throws SystemException {
		if (platform == Platform.weixin) {
			return 1;
		} else if (platform == Platform.alipay) {
			return 2;
		} else {
			throw new SystemException("支付方式错误");
		}
	}

	/** 发送请求 */
	private String send(String url, DaoerBaseRequest request) throws MyException {
		DaoerBaseRequest.RequestMethod method = request.getRequestMethod();
		Map<String, String> map = jsonBean2Map(request);
		Map<String, Object> map1 = new HashMap<>(map.size());
		for (Map.Entry<String, String> entry : map.entrySet()) {
			map1.put(entry.getKey(), entry.getValue());
		}
		String result = null;
		try {
			switch (method) {
			case GET: {
				result = get(url, map1);
			}
				break;
			case POST: {
				result = post(url, map1);
			}
				break;
			default:
			}
		} catch (Exception e) {
			if ("Read timed out".equals(e.getMessage())) {
				throw new MyException("查询超时");
			}
			e.printStackTrace();
		}
		return result;
	}

	/** 道尔返回成功json */
	private String returnSuccess(List<String> ids) {
		String idstr = StringUtils.join(ids, ",");
		return "{\"head\":{\"status\":1,\"message\":\"\"},\"body\":{\"ids\":\"" + (idstr == null ? "" : idstr) + "\"}}";
	}

	/** 道尔返回失败json */
	private String returnErr(String err) {
		return "{\"head\":{\"status\":0,\"message\":\"" + (err == null ? "" : err) + "\"},\"body\":{}}";
	}
}
